```python
import asyncio
from typing import List

from tenacity import retry, stop_after_attempt, wait_exponential

from app.config import config
from app.logger import logger
from app.tool.base import BaseTool
from app.tool.search import (
    BaiduSearchEngine,
    BingSearchEngine,
    DuckDuckGoSearchEngine,
    GoogleSearchEngine,
    WebSearchEngine,
)


class WebSearch(BaseTool):
    """
    WebSearch类继承自BaseTool，用于执行网络搜索并返回相关链接列表。
    """
    name: str = "web_search"
    description: str = """执行网络搜索并返回相关链接列表。
    该功能尝试使用主搜索引擎API获取最新结果。如果发生错误，则回退到备用搜索引擎。"""
    parameters: dict = {
        "type": "object",
        "properties": {
            "query": {
                "type": "string",
                "description": "(必需) 提交到搜索引擎的搜索查询。",
            },
            "num_results": {
                "type": "integer",
                "description": "(可选) 返回的搜索结果数量。默认是10。",
                "default": 10,
            },
        },
        "required": ["query"],
    }
    _search_engine: dict[str, WebSearchEngine] = {
        "google": GoogleSearchEngine(),
        "baidu": BaiduSearchEngine(),
        "duckduckgo": DuckDuckGoSearchEngine(),
        "bing": BingSearchEngine(),
    }

    async def execute(self, query: str, num_results: int = 10) -> List[str]:
        """
        执行网络搜索并返回URL列表。
        按照配置顺序尝试搜索引擎，如果某个引擎失败则回退到下一个引擎。
        如果所有引擎都失败，则等待并重试，直到达到配置的重试次数。

        Args:
            query (str): 提交到搜索引擎的搜索查询。
            num_results (int, optional): 返回的搜索结果数量。默认是10。

        Returns:
            List[str]: 匹配搜索查询的URL列表。
        """
        # 从配置中获取重试设置
        retry_delay = 60  # 默认60秒
        max_retries = 3  # 默认重试3次

        if config.search_config:
            retry_delay = getattr(config.search_config, "retry_delay", 60)
            max_retries = getattr(config.search_config, "max_retries", 3)

        # 当所有引擎失败时尝试重试
        for retry_count in range(
            max_retries + 1
        ):  # +1是因为第一次尝试不算重试
            links = await self._try_all_engines(query, num_results)
            if links:
                return links

            if retry_count < max_retries:
                # 所有引擎失败，等待并重试
                logger.warning(
                    f"所有搜索引擎失败。等待{retry_delay}秒后重试 {retry_count + 1}/{max_retries}..."
                )
                await asyncio.sleep(retry_delay)
            else:
                logger.error(
                    f"所有搜索引擎在{max_retries}次重试后失败。放弃。"
                )

        return []

    async def _try_all_engines(self, query: str, num_results: int) -> List[str]:
        """
        按照配置顺序尝试所有搜索引擎。

        Args:
            query (str): 提交到搜索引擎的搜索查询。
            num_results (int): 返回的搜索结果数量。

        Returns:
            List[str]: 匹配搜索查询的URL列表，如果所有引擎失败则返回空列表。
        """
        engine_order = self._get_engine_order()
        failed_engines = []

        for engine_name in engine_order:
            engine = self._search_engine[engine_name]
            try:
                logger.info(f"🔎 尝试使用{engine_name.capitalize()}进行搜索...")
                links = await self._perform_search_with_engine(
                    engine, query, num_results
                )
                if links:
                    if failed_engines:
                        logger.info(
                            f"使用{engine_name.capitalize()}搜索成功，之前尝试过: {', '.join(failed_engines)}"
                        )
                    return links
            except Exception as e:
                failed_engines.append(engine_name.capitalize())
                is_rate_limit = "429" in str(e) or "Too Many Requests" in str(e)

                if is_rate_limit:
                    logger.warning(
                        f"⚠️ {engine_name.capitalize()} 搜索引擎速率限制超出，尝试下一个引擎..."
                    )
                else:
                    logger.warning(
                        f"⚠️ {engine_name.capitalize()} 搜索失败，错误: {e}"
                    )

        if failed_engines:
            logger.error(f"所有搜索引擎失败: {', '.join(failed_engines)}")
        return []

    def _get_engine_order(self) -> List[str]:
        """
        确定尝试搜索引擎的顺序。
        首选引擎在前（基于配置），其次是备用引擎，然后是其余引擎。

        Returns:
            List[str]: 有序的搜索引擎名称列表。
        """
        preferred = "google"
        fallbacks = []

        if config.search_config:
            if config.search_config.engine:
                preferred = config.search_config.engine.lower()
            if config.search_config.fallback_engines:
                fallbacks = [
                    engine.lower() for engine in config.search_config.fallback_engines
                ]

        engine_order = []
        # 首先添加首选引擎
        if preferred in self._search_engine:
            engine_order.append(preferred)

        # 按顺序添加配置的备用引擎
        for fallback in fallbacks:
            if fallback in self._search_engine and fallback not in engine_order:
                engine_order.append(fallback)

        return engine_order

    @retry(
        stop=stop_after_attempt(3),
        wait=wait_exponential(multiplier=1, min=1, max=10),
    )
    async def _perform_search_with_engine(
        self,
        engine: WebSearchEngine,
        query: str,
        num_results: int,
    ) -> List[str]:
        """
        使用指定的搜索引擎执行搜索。

        Args:
            engine (WebSearchEngine): 要使用的搜索引擎。
            query (str): 提交到搜索引擎的搜索查询。
            num_results (int): 返回的搜索结果数量。

        Returns:
            List[str]: 匹配搜索查询的URL列表。
        """
        loop = asyncio.get_event_loop()
        return await loop.run_in_executor(
            None, lambda: list(engine.perform_search(query, num_results=num_results))
        )
```

这段代码定义了一个 `WebSearch` 类，用于执行网络搜索并返回相关链接列表。代码中包含了详细的注释，解释了每个函数的作用和关键点，确保代码的可读性和维护性。